package com.dailydev.jira.portal;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import com.atlassian.jira.ofbiz.OfBizDelegator;
import com.atlassian.jira.portal.PortalPageConfiguration;
import com.atlassian.jira.portal.PortletConfigurationException;
import com.atlassian.jira.portal.PortletConfigurationManager;
import com.atlassian.jira.portal.ProfessionalPortalManager;
import com.atlassian.jira.util.CollectionReorderer;
import com.atlassian.plugin.PluginManager;
import com.opensymphony.user.EntityNotFoundException;
import com.opensymphony.user.User;

/**
 * Class {@link DasboardSharingPortalManager}
 * 
 * @author Roman Bosak, roman.bosak@gmail.com
 */
public class DasboardSharingPortalManager extends ProfessionalPortalManager {

    protected static Logger logger = Logger
            .getLogger(DasboardSharingPortalManager.class);

    protected IDashboardSharingConfiguration configuration;

    private final IJiraFacade jiraFacade;

    /**
     * @param pluginManager
     * @param delegator
     * @param collectionReorderer
     * @param portletConfigurationManager
     */
    public DasboardSharingPortalManager(final PluginManager pluginManager,
            final OfBizDelegator delegator,
            final CollectionReorderer collectionReorderer,
            final PortletConfigurationManager portletConfigurationManager) {
        this(pluginManager, delegator, collectionReorderer,
                portletConfigurationManager,
                new DashboardSharingConfiguration(), new JiraFacade());
    }

    /**
     * @param pluginManager
     * @param delegator
     * @param collectionReorderer
     * @param portletConfigurationManager
     * @param configuration
     * @param jiraFacade
     */
    public DasboardSharingPortalManager(final PluginManager pluginManager,
            final OfBizDelegator delegator,
            final CollectionReorderer collectionReorderer,
            final PortletConfigurationManager portletConfigurationManager,
            IDashboardSharingConfiguration configuration, IJiraFacade jiraFacade) {
        super(pluginManager, delegator, collectionReorderer,
                portletConfigurationManager);
        this.configuration = configuration;
        this.jiraFacade = jiraFacade;

        if (logger.isDebugEnabled()) {
            logger.debug("Instance created");
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.atlassian.jira.portal.AbstractPortalManager#decreasePageConfigurationSequence(com.opensymphony.user.User,
     *      int)
     */
    @Override
    public void decreasePageConfigurationSequence(final User remoteUser,
            final int position) throws PortletConfigurationException {
        final int ownPages = getOwnPortalPageConfigurations(remoteUser).size();

        if (ownPages > position + 1) {
            super.decreasePageConfigurationSequence(remoteUser, position);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.atlassian.jira.portal.AbstractPortalManager#deletePortalPageConfiguration(com.opensymphony.user.User,
     *      java.lang.Long)
     */
    @Override
    public void deletePortalPageConfiguration(final User remoteUser,
            final Long portletId) throws PortletConfigurationException {

        PortalPageConfiguration portalPage = super.getPortalPageConfiguration(
                remoteUser, portletId);
        if (portalPage != null && portalPage.getId().equals(portletId)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Request for delete portal page: User '"
                        + remoteUser.getName() + "' is owner of portal page '"
                        + portletId + "' -> DELETE");
            }
            super.deletePortalPageConfiguration(remoteUser, portletId);
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Request for delete portal page: User '"
                        + remoteUser.getName()
                        + "' is not owner of portal page '" + portletId
                        + "' -> NOT DELETED");
            }
        }
    }

    /**
     * @param user
     * @return
     * @throws PortletConfigurationException
     */
    public Map<Long, PortalPageConfiguration> getOwnPortalPageConfigurations(
            final User user) throws PortletConfigurationException {
        return super.getPortalPageConfigurations(user);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.atlassian.jira.portal.AbstractPortalManager#getPortalPageConfiguration(com.opensymphony.user.User,
     *      java.lang.Long)
     */
    @Override
    public PortalPageConfiguration getPortalPageConfiguration(User user,
            Long pageId) throws PortletConfigurationException {
        if (logger.isDebugEnabled()) {
            logger.debug(MessageFormat.format(
                    "getPortalPageConfiguration({0}, {1})", user.getName(),
                    pageId));
        }
        String dashboardAuthor = configuration.getDashboardAuthor(pageId);

        if (dashboardAuthor != null && !dashboardAuthor.equals(user.getName())) {
            try {
                return this.getPortalPageConfiguration(dashboardAuthor, pageId);
            } catch (final EntityNotFoundException e) {
                logger.warn("Unable to find author of shared dashboard> "
                        + dashboardAuthor, e);
            }
        }

        return super.getPortalPageConfiguration(user, pageId);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.atlassian.jira.portal.AbstractPortalManager#getPortalPageConfigurations(com.opensymphony.user.User)
     */
    @Override
    public Map getPortalPageConfigurations(final User user)
            throws PortletConfigurationException {
        if (!isUsingDefaultPortalPageConfigurations(user)) {

            // get own portal page configurations
            final Map<Long, PortalPageConfiguration> portalPages = getOwnPortalPageConfigurations(user);
            portalPages.putAll(portalPages);

            if (logger.isDebugEnabled()) {
                logger.debug("Users (" + user.getName()
                        + ") original portal pages: " + portalPages.keySet());
            }

            try {
                // iterate through all groups for which there are dashboards
                // configured
                for (final String groupName : configuration
                        .getDashboardGroups()) {

                    // check if user has view permission for the group
                    if (jiraFacade.hasViewGroupPermission(groupName, user)) {

                        // get dashboards shared for group (except dashboards
                        // authored by user)
                        Map<Long, PortalPageConfiguration> dashboardsForGroup = getDashboardsForGroup(
                                groupName, user);
                        portalPages.putAll(dashboardsForGroup);

                        if (logger.isDebugEnabled()) {
                            logger.debug("Users (" + user.getName()
                                    + ") shared portal pages from group ("
                                    + groupName + "): "
                                    + dashboardsForGroup.keySet());
                        }
                    }
                }
            } catch (Throwable e) {
                logger.error("Error while geting shared portal pages", e);
            }
            return portalPages;
        }
        return getOwnPortalPageConfigurations(user);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.atlassian.jira.portal.AbstractPortalManager#hasAccessToPage(com.opensymphony.user.User,
     *      java.lang.Long)
     */
    @Override
    public boolean hasAccessToPage(final User user, final Long portalPageId) {
        try {
            final Set<String> groupNames = configuration.getDashboardGroups();

            for (final Object element : groupNames) {
                final String groupName = (String) element;
                if (jiraFacade.hasViewGroupPermission(groupName, user)) {
                    final Set<Long> dashboards = configuration
                            .getGroupDashboards(groupName);

                    if (dashboards.contains(portalPageId)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("User '" + user
                                    + "' has acces to portal page '"
                                    + portalPageId
                                    + "' because it is shared for his group '"
                                    + groupName + "'");
                        }
                        return true;
                    }
                }
            }
        } catch (Throwable e) {
            logger
                    .error(
                            "Error while resolving if user has access to shared portal page",
                            e);
        }

        return super.hasAccessToPage(user, portalPageId);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.atlassian.jira.portal.AbstractPortalManager#increasePageConfigurationSequence(com.opensymphony.user.User,
     *      int)
     */
    @Override
    public void increasePageConfigurationSequence(final User remoteUser,
            final int position) throws PortletConfigurationException {
        final int ownPages = getOwnPortalPageConfigurations(remoteUser).size();

        if (ownPages > position) {
            super.increasePageConfigurationSequence(remoteUser, position);
        }
    }

    /**
     * @param groupName
     * @param user
     * @return {@link Map} of dashboard IDs to dashboard instances ({@link PortalPageConfiguration})
     *         for dashboards shared for <code>groupName</code> except
     *         dashboards authored by <code>user</code>
     * @throws PortletConfigurationException
     */
    protected Map<Long, PortalPageConfiguration> getDashboardsForGroup(
            final String groupName, final User user)
            throws PortletConfigurationException {
        Map<Long, PortalPageConfiguration> dashboardsForGroup = new HashMap<Long, PortalPageConfiguration>();

        // get list of dashboard IDs of dashboards shared for group
        Set<Long> dashboards = configuration.getGroupDashboards(groupName);

        // iterate through dashboard IDs of dashboards shared for group
        for (final Long dashboardId : dashboards) {
            // get name of author of the dashboard
            String authorName = configuration.getDashboardAuthor(dashboardId);

            // check if author is not the user
            if (!user.getName().equals(authorName)) {
                try {
                    PortalPageConfiguration dashboard = getPortalPageConfiguration(
                            authorName, dashboardId);

                    // checks if the right dashboard was found
                    if (dashboard != null
                            && dashboard.getId().equals(dashboardId)) {
                        dashboard.setPageName(dashboard.getPageName()
                                + " (Shared)");

                        dashboardsForGroup.put(dashboardId, dashboard);
                    } else {
                        logger.warn("Unable to find dashboard '" + dashboardId
                                + "' of '" + authorName + "'");
                    }
                } catch (final EntityNotFoundException e) {
                    logger.warn("Unable to find author of shared dashboard> "
                            + authorName, e);
                }
            }
        }
        return dashboardsForGroup;
    }

    /**
     * @param authorName
     * @param dashboardId
     * @return
     * @throws PortletConfigurationException
     * @throws EntityNotFoundException
     */
    protected PortalPageConfiguration getPortalPageConfiguration(
            final String authorName, final Long dashboardId)
            throws PortletConfigurationException, EntityNotFoundException {
        User author = jiraFacade.getUser(authorName);
        return getPortalPageConfiguration(author, dashboardId);
    }

}
